package com.mes.common.dao;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.impl.CriteriaImpl;

import com.bstek.bdf2.core.orm.hibernate.HibernateDao;
import com.bstek.bdf2.core.orm.hibernate.ISessionCallback;
import com.bstek.dorado.data.provider.Page;

public abstract class MMHibernateDao<T> extends HibernateDao {
	
	protected void add(final T t) throws Exception {
		Session session = this.getSessionFactory().getCurrentSession();
		try {
			session.save(t);
		} finally {
			session.flush();
		}
		
//		this.doInHibernateSession(new ISessionCallback<T>() {
//			@SuppressWarnings("unchecked")
//			public T doInSession(Session session) {
//				return (T) session.save(t);
//			}
//		});
	}

	protected void update(final T t) throws Exception {
		Session session = this.getSessionFactory().getCurrentSession();
		try {
			session.update(t);
		} finally {
			session.flush();
		}
		
//		this.doInHibernateSession(new ISessionCallback<T>() {
//			public T doInSession(Session session) {
//				session.update(t);
//				return null;
//			}
//		});
	}

	protected void delete(final T t) throws Exception {
		Session session = this.getSessionFactory().getCurrentSession();
		try {
			session.delete(t);
		} finally {
			session.flush();
		}
		
//		this.doInHibernateSession(new ISessionCallback<T>() {
//			public T doInSession(Session session) {
//				session.delete(t);
//				return null;
//			}
//		});
	}

	@SuppressWarnings("unchecked")
	protected List<T> loadAll(final Class<T> clazz) throws Exception {
		Session session = this.getSessionFactory().getCurrentSession();
		return (List<T>) session.createQuery("from " + clazz.getName()).list();
	}

	@SuppressWarnings("unchecked")
	protected T load(final Class<T> clazz, final java.io.Serializable id) throws Exception {
		Session session = this.getSessionFactory().getCurrentSession();
		return (T)session.load(clazz, id);
	}

	@SuppressWarnings("unchecked")
	protected T get(final Class<T> clazz, final java.io.Serializable id) throws Exception {
		Session session = this.getSessionFactory().getCurrentSession();		
		return (T) session.get(clazz, id);
	}

	@Override
	public void pagingQuery(final Page<?> page, final String queryString, final String countQueryString,
			final Object[] parameters, final Map<String, Object> parametersMap, String dataSourceName)
			throws Exception {
		Session session = this.getSessionFactory(dataSourceName).getCurrentSession();
		String queryHql = queryString.trim();
		Query query = session.createQuery(queryHql);
		int safePageSize = page.getPageSize() < 1 ? 65535 : page.getPageSize();
		int start = (page.getPageNo() - 1) * safePageSize;
		if (parameters != null) {
			setQueryParameters(query, parameters);
		} else if (parametersMap != null) {
			setQueryParameters(query, parametersMap);
		}
		query.setMaxResults(safePageSize).setFirstResult(start);
		page.setEntities(query.list());
		Query countQuery = session.createQuery(countQueryString);
		if (parameters != null) {
			setQueryParameters(countQuery, parameters);
		} else if (parametersMap != null) {
			setQueryParameters(countQuery, parametersMap);
		}
		int count = 0;
		Object countObj = countQuery.uniqueResult();
		if (countObj instanceof Long) {
			count = ((Long) countObj).intValue();
		} else if (countObj instanceof Integer) {
			count = ((Integer) countObj).intValue();
		}
		page.setEntityCount(count);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void pagingQuery(Page<?> page, DetachedCriteria detachedCriteria, String dataSourceName) {
		Session session = this.getSessionFactory(dataSourceName).getCurrentSession();
		Criteria criteria = detachedCriteria.getExecutableCriteria(session);
		CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;
		List orderEntrys = new ArrayList();
		try {
			Field field = CriteriaImpl.class.getDeclaredField("orderEntries");
			field.setAccessible(true);
			orderEntrys = (List) field.get(criteriaImpl);
			field.set(criteria, new ArrayList());
		} catch (Exception e) {
			e.printStackTrace();
		}
		Projection projection = criteriaImpl.getProjection();
		int totalCount = 0;
		Object totalObject = criteria.setProjection(Projections.rowCount()).uniqueResult();
		if (totalObject instanceof Integer) {
			totalCount = (Integer) totalObject;
		} else if (totalObject instanceof Long) {
			totalCount = ((Long) totalObject).intValue();
		} else if (totalObject != null) {
			throw new RuntimeException(
					"Can not convert [" + totalObject + "] to a int value when query entity total count!");
		}
		page.setEntityCount(totalCount);

		try {
			Field field = CriteriaImpl.class.getDeclaredField("orderEntries");
			field.setAccessible(true);
			for (int i = 0; i < orderEntrys.size(); i++) {
				List innerOrderEntries = (List) field.get(criteriaImpl);
				innerOrderEntries.add(orderEntrys.get(i));
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		criteria.setProjection(projection);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		int start = (page.getPageNo() - 1) * page.getPageSize();
		page.setEntities(detachedCriteria.getExecutableCriteria(session).setFirstResult(start)
				.setMaxResults(page.getPageSize()).list());
	}

	@Override
	public List<T> query(DetachedCriteria detachedCriteria, String dataSourceName) {
		Session session = this.getSessionFactory(dataSourceName).getCurrentSession();
		return detachedCriteria.getExecutableCriteria(session).list();
	}

	public List<T> query(DetachedCriteria detachedCriteria, int pageIndex, int pageSize, String dataSourceName) {
		Session session = this.getSessionFactory(dataSourceName).getCurrentSession();
		return detachedCriteria.getExecutableCriteria(session).setFirstResult((pageIndex - 1) * pageSize)
				.setMaxResults(pageSize).list();
	}

	public <T> List<T> query(String hql, String dataSourceName) {
		return this.query(hql, null, null, dataSourceName);
	}

	public <T> List<T> query(String hql) {
		return this.query(hql, null, null, null);
	}

	public <T> List<T> query(String hql, Map<String, Object> parameterMap) {
		return this.query(hql, null, parameterMap, null);
	}

	public <T> List<T> query(String hql, Map<String, Object> parameterMap, String dataSourceName) {
		return this.query(hql, null, parameterMap, dataSourceName);
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> query(String hql, Object[] parameters, Map<String, Object> parameterMap,
			String dataSourceName) {
		Session session = this.getSessionFactory(dataSourceName).getCurrentSession();
		List<T> result = null;
		Query query = session.createQuery(hql);
		if (parameters != null) {
			this.setQueryParameters(query, parameters);
		} else if (parameterMap != null) {
			this.setQueryParameters(query, parameterMap);
		}
		result = query.list();
		return result;
	}

	@Override
	public List<T> query(String queryString, Map<String, Object> parametersMap, int pageIndex, int pageSize,
			String dataSourceName) {
		Session session = this.getSessionFactory(dataSourceName).getCurrentSession();
		Query query = session.createQuery(queryString);
		return query.setFirstResult((pageIndex - 1) * pageSize).setMaxResults(pageSize).list();
	}

}
